#!/usr/bin/python

from functions import *

pattern = re.compile(r'([|&!()]|([+-]?(\w*:)?\w+))')

class Search:
    def __init__(self, string):
        self.string = string
        self.token = self.tokens().next
        self.group = self.parse()

    def search(self, witness):
        return self.group.search(witness)

    def parse(self):
        lhs = Group()
        tok = self.token()
        while tok:
            if tok == ')':
                return lhs 
            elif tok == '(':
                lhs = self.parse()
            elif tok in '|&':
                rhs = self.parse()
                return AndOr(tok, lhs, rhs)
            elif tok == '!':
                rhs = self.parse()
                return Not(rhs)
            else:
                lhs.append(Term(tok))
            tok = self.token()
        return lhs

    def tokens(self):
        for tok in pattern.findall(self.string):
            yield tok[0]
        yield None

    def __repr__(self):
        return 'Search(' + str(self.group) +')'

    def __str__(self):
        return `self`

class Group(list):
    def search(self, witness):
        for term in self:
            if not term.search(witness):
                return False
        return True

class AndOr:
    def __init__(self, op, lhs, rhs):
        self.op = op
        self.lhs = lhs
        self.rhs = rhs

    def search(self, witness):
        if self.op == '&':
            return self.lhs.search(witness) and self.rhs.search(witness) 
        elif self.op == '|':
            return self.lhs.search(witness) or self.rhs.search(witness)

    def __repr__(self):
        return '(' + self.op + ',' + str(self.lhs) + ',' + str(self.rhs) + ')'

    def __str__(self):
        return `self`

class Not:
    def __init__(self, rhs):
        self.op = '!'
        self.rhs = rhs

    def search(self, witness):
        return not self.rhs.search(witness) 

    def __repr__(self):
        return '(' + self.op + ',' + str(self.rhs) + ')'

class Term:
    def __init__(self, string):
        self.string = string
        self.type = None
        if string.startswith('-'):
            self.opcode = '-'
            phrase = string[1:]
        elif string.startswith('+'):
            self.opcode = '+'
            phrase = string[1:]
        else:
            self.opcode = '+'
            phrase = string

        if phrase.find(':') != -1:
            type, phrase = phrase.split(':')
            self.type = type.upper()
        if len(phrase) == 7 and phrase[6].isdigit():
            self.phrase = phrase
        else:
            self.phrase = soundex(phrase)

    def __repr__(self):
        type = self.type if self.type else '*'
        return self.opcode + type + ':' + self.phrase

    def search(self, witness):
        if self.type:
            prayers = [ p for p in witness.prayers if p.type() == self.type ]
        else:
            prayers = witness.prayers
        if not prayers: return False
        for p in prayers:
            if self.phrase in p.sounds and self.opcode == '+': return True
            if self.phrase in p.sounds and self.opcode == '-': return False
        if self.opcode == '-':
            return True
        else:
            return False

